home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / pagecurl / pagecurl.c < prev   
Encoding:
C/C++ Source or Header  |  2003-01-15  |  31.4 KB  |  1,117 lines

  1. /* Page Curl 0.9 --- image filter plug-in for The Gimp
  2.  * Copyright (C) 1996 Federico Mena Quintero
  3.  * Ported to Gimp 1.0 1998 by Simon Budig <Simon.Budig@unix-ag.org>
  4.  *
  5.  * You can contact me at quartic@polloux.fciencias.unam.mx
  6.  * You can contact the original The Gimp authors at gimp@xcf.berkeley.edu
  7.  *
  8.  * This program is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; either version 2 of the License, or
  11.  * (at your option) any later version.
  12.  *
  13.  * This program is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with this program; if not, write to the Free Software
  20.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21.  *
  22.  */
  23.  
  24. /* TODO for v0.5 - in 0.9 still to do...
  25.  * As of version 0.5 alpha, the only thing that is not yet implemented
  26.  * is the "Warp curl" option.  Everything else seems to be working
  27.  * just fine.  Please email me if you find any bugs.  I know that the
  28.  * calculation code is horrible, but you don't want to tweak it anyway ;)
  29.  */
  30.  
  31. /*
  32.  * Ported to the 0.99.x architecture by Simon Budig, Simon.Budig@unix-ag.org
  33.  */
  34.  
  35. /*
  36.  * Version History
  37.  * 0.5: (1996) Version for Gimp 0.54 by Federico Mena Quintero
  38.  * 0.6: (Feb '98) First Version for Gimp 0.99.x, very buggy.
  39.  * 0.8: (Mar '98) First "stable" version
  40.  * 0.9: (May '98)
  41.  *      - Added support for Gradients. It is now possible to map
  42.  *        a gradient to the back of the curl.
  43.  *      - This implies a changed PDB-Interface: New "mode" parameter.
  44.  *      - Pagecurl now returns the ID of the new layer.
  45.  *      - Exchanged the meaning of FG/BG Color, because mostly the FG
  46.  *        color is darker.
  47.  */
  48. #include "config.h"
  49.  
  50. #include <stdio.h>
  51. #include <stdlib.h>
  52.  
  53. #include <gtk/gtk.h>
  54.  
  55. #include <libgimp/gimp.h>
  56. #include <libgimp/gimpui.h>
  57.  
  58. #include "libgimp/stdplugins-intl.h"
  59.  
  60. #include "curl0.xpm"
  61. #include "curl1.xpm"
  62. #include "curl2.xpm"
  63. #include "curl3.xpm"
  64. #include "curl4.xpm"
  65. #include "curl5.xpm"
  66. #include "curl6.xpm"
  67. #include "curl7.xpm"
  68.  
  69. #define PLUG_IN_NAME    "plug_in_pagecurl"
  70. #define PLUG_IN_VERSION "May 1998, 0.9"
  71. #define NGRADSAMPLES    256
  72.  
  73.  
  74. /***** Types *****/
  75.  
  76. typedef struct
  77. {
  78.   gint    do_curl_shade;
  79.   gint    do_curl_gradient;
  80.   gint    do_curl_warp;  /* Not yet supported... */
  81.  
  82.   gdouble do_curl_opacity;
  83.   gint    do_shade_under;
  84.  
  85.   gint    do_upper_left;
  86.   gint    do_upper_right;
  87.   gint    do_lower_left;
  88.   gint    do_lower_right;
  89.  
  90.   gint    do_vertical;
  91.   gint    do_horizontal;
  92. } CurlParams;
  93.  
  94. /***** Prototypes *****/
  95.  
  96. static void query (void);
  97. static void run   (gchar      *name,
  98.            gint        nparams,
  99.            GimpParam  *param,
  100.            gint       *nreturn_vals,
  101.            GimpParam **return_vals);
  102.  
  103. static void set_default_params (void);
  104.  
  105. static void dialog_ok_callback   (GtkWidget     *widget,
  106.                   gpointer       data);
  107. static void dialog_toggle_update (GtkWidget     *widget,
  108.                   gint32         value);
  109. static void dialog_scale_update  (GtkAdjustment *adjustment,
  110.                   gdouble       *value);
  111.  
  112. static gint do_dialog (void);
  113.  
  114. static void init_calculation (void);
  115.  
  116. static gint left_of_diagl  (gdouble x, gdouble y);
  117. static gint right_of_diagr (gdouble x, gdouble y);
  118. static gint below_diagb    (gdouble x, gdouble y);
  119. static gint right_of_diagm (gdouble x, gdouble y);
  120. static gint inside_circle  (gdouble x, gdouble y);
  121.  
  122. static void    do_curl_effect      (void);
  123. static void    clear_curled_region (void);
  124. static void    page_curl           (void);
  125. static guchar *get_samples         (GimpDrawable *drawable);
  126.  
  127. /***** Variables *****/
  128.  
  129. GimpPlugInInfo PLUG_IN_INFO =
  130. {
  131.   NULL,  /* init_proc  */
  132.   NULL,  /* quit_proc  */
  133.   query, /* query_proc */
  134.   run    /* run_proc   */
  135. };
  136.  
  137. static CurlParams curl;
  138.  
  139. /* Image parameters */
  140.  
  141. static gint32     image_id;
  142. static GimpDrawable *curl_layer;
  143. static GimpDrawable *drawable;
  144.  
  145. static GdkPixmap *gdk_curl_pixmaps[8];
  146. static GdkBitmap *gdk_curl_masks[8];
  147.  
  148. static GtkWidget *curl_pixmap_widget;
  149.  
  150. static gint   sel_x1, sel_y1, sel_x2, sel_y2;
  151. static gint   true_sel_width, true_sel_height;
  152. static gint   sel_width, sel_height;
  153. static gint   drawable_position;
  154. static gint   curl_run = FALSE;
  155. static gint32 curl_layer_ID;
  156.  
  157. /* Center and radius of circle */
  158.  
  159. static GimpVector2 center;
  160. static double      radius;
  161.  
  162. /* Useful points to keep around */
  163.  
  164. static GimpVector2 left_tangent;
  165. static GimpVector2 right_tangent;
  166.  
  167. /* Slopes --- these are *not* in the usual geometric sense! */
  168.  
  169. static gdouble diagl_slope;
  170. static gdouble diagr_slope;
  171. static gdouble diagb_slope;
  172. static gdouble diagm_slope;
  173.  
  174. /* User-configured parameters */
  175.  
  176. static guchar fore_color[3];
  177. static guchar back_color[3];
  178.  
  179.  
  180. /***** Functions *****/
  181.  
  182. MAIN ()
  183.  
  184. static void
  185. query (void)
  186. {
  187.   static GimpParamDef args[] =
  188.   {
  189.     { GIMP_PDB_INT32, "run_mode", "Interactive (0), non-interactive (1)" },
  190.     { GIMP_PDB_IMAGE, "image", "Input image" },
  191.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  192.     { GIMP_PDB_INT32, "mode", "Pagecurl-mode: Use FG- and BG-Color (0), Use current gradient (1)" },
  193.     { GIMP_PDB_INT32, "edge", "Edge to curl (1-4, clockwise, starting in the lower right edge)" },
  194.     { GIMP_PDB_INT32, "type", "vertical (0), horizontal (1)" },
  195.     { GIMP_PDB_INT32, "shade", "Shade the region under the curl (1) or not (0)" },
  196.   };
  197.   static gint nargs = sizeof (args) / sizeof (args[0]);
  198.  
  199.   static GimpParamDef return_vals[] =
  200.   {
  201.     { GIMP_PDB_LAYER, "Curl Layer", "The new layer with the curl." }
  202.   }; 
  203.   static gint nreturn_vals = sizeof (return_vals) / sizeof (return_vals[0]);
  204.  
  205.   INIT_I18N();
  206.  
  207.   gimp_install_procedure (PLUG_IN_NAME,
  208.               "Pagecurl effect",
  209.               "This plug-in creates a pagecurl-effect.",
  210.               "Federico Mena Quintero and Simon Budig",
  211.               "Federico Mena Quintero and Simon Budig",
  212.               PLUG_IN_VERSION,
  213.               N_("<Image>/Filters/Distorts/Pagecurl..."),
  214.               "RGB*, GRAY*",
  215.               GIMP_PLUGIN,
  216.               nargs,
  217.               nreturn_vals,
  218.               args,
  219.               return_vals);
  220. }
  221.  
  222. static void
  223. run (gchar      *name,
  224.      gint        nparams,
  225.      GimpParam  *param,
  226.      gint       *nreturn_vals,
  227.      GimpParam **return_vals)
  228. {
  229.   static GimpParam  values[2];
  230.   GimpRunModeType   run_mode;
  231.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  232.  
  233.   run_mode = param[0].data.d_int32;
  234.  
  235.   set_default_params ();
  236.  
  237.   /*  Possibly retrieve data  */
  238.   gimp_get_data (PLUG_IN_NAME, &curl);
  239.  
  240.   *nreturn_vals = 2;
  241.   *return_vals = values;
  242.  
  243.   values[0].type = GIMP_PDB_STATUS;
  244.   values[0].data.d_status = status;
  245.   values[1].type = GIMP_PDB_LAYER;
  246.   values[1].data.d_layer = -1;
  247.  
  248.   /*  Get the specified drawable  */
  249.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  250.   image_id = param[1].data.d_image;
  251.  
  252.   if (gimp_drawable_is_rgb (drawable->id)
  253.       || gimp_drawable_is_gray (drawable->id))
  254.     {
  255.       switch (run_mode)
  256.     {
  257.     case GIMP_RUN_INTERACTIVE:
  258.       INIT_I18N_UI();
  259.       /*  First acquire information with a dialog  */
  260.       if (!do_dialog ())
  261.         return;
  262.       break;
  263.  
  264.     case GIMP_RUN_NONINTERACTIVE:
  265.       INIT_I18N();
  266.       /*  Make sure all the arguments are there!  */
  267.       if (nparams != 7)
  268.         status = GIMP_PDB_CALLING_ERROR;
  269.       if (status == GIMP_PDB_SUCCESS)
  270.         {
  271.           curl.do_curl_shade = (param[3].data.d_int32 == 0) ? 1 : 0;
  272.           curl.do_curl_gradient = 1 - curl.do_curl_shade;
  273.           switch (param[4].data.d_int32)
  274.         {
  275.         case 1:
  276.           curl.do_upper_left = 0;
  277.           curl.do_upper_right = 0;
  278.           curl.do_lower_left = 0;
  279.           curl.do_lower_right = 1;
  280.           break;
  281.         case 2:
  282.           curl.do_upper_left = 0;
  283.           curl.do_upper_right = 0;
  284.           curl.do_lower_left = 1;
  285.           curl.do_lower_right = 0;
  286.           break;
  287.         case 3:
  288.           curl.do_upper_left = 1;
  289.           curl.do_upper_right = 0;
  290.           curl.do_lower_left = 0;
  291.           curl.do_lower_right = 0;
  292.           break;
  293.         case 4:
  294.           curl.do_upper_left = 0;
  295.           curl.do_upper_right = 1;
  296.           curl.do_lower_left = 0;
  297.           curl.do_lower_right = 0;
  298.           break;
  299.         default:
  300.           break;
  301.         }
  302.           curl.do_vertical    = (param[5].data.d_int32) ? 0 : 1;
  303.           curl.do_horizontal  = 1 - curl.do_vertical;
  304.           curl.do_shade_under = (param[6].data.d_int32) ? 1 : 0;
  305.         }
  306.       break;
  307.  
  308.     case GIMP_RUN_WITH_LAST_VALS:
  309.       INIT_I18N();
  310.       break;
  311.  
  312.     default:
  313.       break;
  314.     }
  315.  
  316.       if (status == GIMP_PDB_SUCCESS)
  317.     {
  318.       page_curl ();
  319.       values[1].data.d_layer = curl_layer_ID;
  320.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  321.             gimp_displays_flush ();
  322.       if (run_mode == GIMP_RUN_INTERACTIVE)
  323.             gimp_set_data (PLUG_IN_NAME, &curl, sizeof (CurlParams));
  324.     }
  325.     }
  326.   else
  327.     /* Sorry - no indexed/noalpha images */
  328.     status = GIMP_PDB_EXECUTION_ERROR;
  329.  
  330.   values[0].data.d_status = status;
  331. }
  332.  
  333. /*****************************************************
  334.  * Functions to locate the current point in the curl.
  335.  *  The following functions assume an curl in the
  336.  *  lower right corner.
  337.  *  diagb crosses the two tangential points from the
  338.  *  circle with diagl and diagr.
  339.  *
  340.  *   +------------------------------------------------+
  341.  *   |                                          __-- /|
  342.  *   |                                      __--   _/ |
  343.  *   |                                  __--      /   |
  344.  *   |                              __--        _/    |
  345.  *   |                          __--           /      |
  346.  *   |                      __--____         _/       |
  347.  *   |                  __----~~    \      _/         |
  348.  *   |              __--             |   _/           |
  349.  *   |    diagl __--               _/| _/diagr        |
  350.  *   |      __--            diagm_/  |/               |
  351.  *   |  __--                    /___/                 |
  352.  *   +-------------------------+----------------------+
  353.  */
  354.  
  355. static gint
  356. left_of_diagl (gdouble x,
  357.            gdouble y)
  358. {
  359.   return (x < (sel_width + (y - sel_height) * diagl_slope));
  360. }
  361.  
  362. static gint
  363. right_of_diagr (gdouble x,
  364.         gdouble y)
  365. {
  366.   return (x > (sel_width + (y - sel_height) * diagr_slope));
  367. }
  368.  
  369. static gint
  370. below_diagb (gdouble x,
  371.          gdouble y)
  372. {
  373.   return (y < (right_tangent.y + (x - right_tangent.x) * diagb_slope));
  374. }
  375.  
  376. static gint
  377. right_of_diagm (gdouble x,
  378.         gdouble y)
  379. {
  380.   return (x > (sel_width + (y - sel_height) * diagm_slope));
  381. }
  382.  
  383. static gint
  384. inside_circle (gdouble x,
  385.            gdouble y)
  386. {
  387.   gdouble dx, dy;
  388.  
  389.   dx = x - center.x;
  390.   dy = y - center.y;
  391.  
  392.   return (sqrt (dx * dx + dy * dy) <= radius);
  393. }
  394.  
  395. static void
  396. set_default_params (void)
  397. {
  398.   curl.do_curl_shade = 1;
  399.   curl.do_curl_gradient = 0;
  400.   curl.do_curl_warp = 0;  /* Not yet supported... */
  401.  
  402.   curl.do_curl_opacity = 1.0;
  403.   curl.do_shade_under = 1;
  404.  
  405.   curl.do_upper_left = 0;
  406.   curl.do_upper_right = 0;
  407.   curl.do_lower_left = 0;
  408.   curl.do_lower_right = 1;
  409.  
  410.   curl.do_vertical = 1;
  411.   curl.do_horizontal = 0;
  412. }
  413.  
  414. /********************************************************************/
  415. /* dialog callbacks                                                 */
  416. /********************************************************************/
  417.  
  418. static void
  419. dialog_ok_callback (GtkWidget *widget,
  420.             gpointer   data)
  421. {
  422.   curl_run = TRUE;
  423.  
  424.   gtk_widget_destroy (GTK_WIDGET (data));
  425. }
  426.  
  427. static void
  428. dialog_scale_update (GtkAdjustment *adjustment,
  429.              gdouble       *value)
  430. {
  431.   *value = ((double) adjustment->value) / 100.0;
  432. }
  433.  
  434. static void
  435. dialog_toggle_update (GtkWidget *widget,
  436.               gint32     value)
  437. {
  438.   gint pixmapindex = 0;
  439.  
  440.   switch (value)
  441.     {
  442.     case 0:
  443.       curl.do_upper_left = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  444.       break;
  445.     case 1:
  446.       curl.do_upper_right = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  447.       break;
  448.     case 2:
  449.       curl.do_lower_left = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  450.       break;
  451.     case 3:
  452.       curl.do_lower_right = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  453.       break;
  454.     case 5:
  455.       curl.do_horizontal = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  456.       break;
  457.     case 6:
  458.       curl.do_vertical = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  459.       break;
  460.     case 8:
  461.       curl.do_shade_under = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  462.       return;
  463.       break;
  464.     case 9:
  465.       curl.do_curl_gradient = (GTK_TOGGLE_BUTTON (widget)->active) ? 1 : 0;
  466.       curl.do_curl_shade = (GTK_TOGGLE_BUTTON (widget)->active) ? 0 : 1;
  467.       return;
  468.       break;
  469.     default:
  470.       break;
  471.     }
  472.  
  473.   if (curl.do_upper_left + curl.do_upper_right +
  474.       curl.do_lower_left + curl.do_lower_right == 1 &&
  475.       curl.do_horizontal + curl.do_vertical == 1)
  476.     {
  477.       pixmapindex = curl.do_lower_left + curl.do_upper_right * 2 +
  478.     curl.do_upper_left * 3 + curl.do_horizontal * 4;
  479.       if (pixmapindex < 0 || pixmapindex > 7)
  480.     pixmapindex = 0;
  481.       gtk_pixmap_set (GTK_PIXMAP (curl_pixmap_widget),
  482.               gdk_curl_pixmaps[pixmapindex],
  483.               gdk_curl_masks[pixmapindex]);
  484.     }
  485. }
  486.  
  487. static gint
  488. do_dialog (void) 
  489. {
  490.   /* Missing options: Color-dialogs? / own curl layer ? / transparency
  491.      to original drawable / Warp-curl (unsupported yet) */
  492.  
  493.   GtkWidget *dialog;
  494.   GtkWidget *hbox;
  495.   GtkWidget *vbox;
  496.   GtkWidget *vbox2;
  497.   GtkWidget *table;
  498.   GtkWidget *frame;
  499.   GtkWidget *shade_button;
  500.   GtkWidget *gradient_button;
  501.   GtkWidget *button;
  502.   GtkWidget *scale;
  503.   GtkStyle  *style;
  504.   GtkObject *adjustment;
  505.   gint pixmapindex;
  506.  
  507.   gimp_ui_init ("pagecurl", FALSE);
  508.  
  509.   dialog = gimp_dialog_new ( _("Pagecurl Effect"), "pagecurl",
  510.                 gimp_standard_help_func, "filters/pagecurl.html",
  511.                 GTK_WIN_POS_MOUSE,
  512.                 FALSE, TRUE, FALSE,
  513.  
  514.                 _("OK"), dialog_ok_callback,
  515.                 NULL, NULL, NULL, TRUE, FALSE,
  516.                 _("Cancel"), gtk_widget_destroy,
  517.                 NULL, 1, NULL, FALSE, TRUE,
  518.  
  519.                 NULL);
  520.  
  521.    gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
  522.                GTK_SIGNAL_FUNC (gtk_main_quit),
  523.                NULL);
  524.  
  525.    vbox = gtk_vbox_new (FALSE, 4);
  526.    gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
  527.    gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, TRUE, TRUE, 0);
  528.    gtk_widget_show (vbox);
  529.  
  530.    frame = gtk_frame_new (_("Curl Location"));
  531.    gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
  532.  
  533.    table = gtk_table_new (3, 3, FALSE);
  534.    gtk_table_set_col_spacings (GTK_TABLE (table), 2);
  535.    gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  536.    gtk_container_set_border_width (GTK_CONTAINER (table), 2);
  537.    gtk_container_add (GTK_CONTAINER (frame), table);
  538.  
  539.    gtk_widget_realize (dialog);
  540.    style = gtk_widget_get_style (dialog);
  541.  
  542.    gdk_curl_pixmaps[0] =
  543.      gdk_pixmap_create_from_xpm_d (dialog->window,
  544.                    &(gdk_curl_masks[0]),
  545.                    &style->bg[GTK_STATE_NORMAL],
  546.                    curl0_xpm);
  547.    gdk_curl_pixmaps[1] =
  548.      gdk_pixmap_create_from_xpm_d (dialog->window,
  549.                    &(gdk_curl_masks[1]),
  550.                    &style->bg[GTK_STATE_NORMAL],
  551.                    curl1_xpm);
  552.    gdk_curl_pixmaps[2] =
  553.      gdk_pixmap_create_from_xpm_d (dialog->window,
  554.                    &(gdk_curl_masks[2]),
  555.                    &style->bg[GTK_STATE_NORMAL],
  556.                    curl2_xpm);
  557.    gdk_curl_pixmaps[3] =
  558.      gdk_pixmap_create_from_xpm_d (dialog->window,
  559.                    &(gdk_curl_masks[3]),
  560.                    &style->bg[GTK_STATE_NORMAL],
  561.                    curl3_xpm);
  562.    gdk_curl_pixmaps[4] =
  563.      gdk_pixmap_create_from_xpm_d (dialog->window,
  564.                    &(gdk_curl_masks[4]),
  565.                    &style->bg[GTK_STATE_NORMAL],
  566.                    curl4_xpm);
  567.    gdk_curl_pixmaps[5] =
  568.      gdk_pixmap_create_from_xpm_d (dialog->window,
  569.                    &(gdk_curl_masks[5]),
  570.                    &style->bg[GTK_STATE_NORMAL],
  571.                    curl5_xpm);
  572.    gdk_curl_pixmaps[6] =
  573.      gdk_pixmap_create_from_xpm_d (dialog->window,
  574.                    &(gdk_curl_masks[6]),
  575.                    &style->bg[GTK_STATE_NORMAL],
  576.                    curl6_xpm);
  577.    gdk_curl_pixmaps[7] =
  578.      gdk_pixmap_create_from_xpm_d (dialog->window,
  579.                    &(gdk_curl_masks[7]),
  580.                    &style->bg[GTK_STATE_NORMAL],
  581.                    curl7_xpm);
  582.  
  583.    pixmapindex = curl.do_lower_left + curl.do_upper_right * 2 +
  584.       curl.do_upper_left * 3 + curl.do_horizontal * 4;
  585.    if (pixmapindex < 0 || pixmapindex > 7)
  586.       pixmapindex = 0;
  587.    curl_pixmap_widget = gtk_pixmap_new (gdk_curl_pixmaps[pixmapindex],
  588.                     gdk_curl_masks[pixmapindex]);
  589.    gtk_table_attach (GTK_TABLE (table), curl_pixmap_widget, 1, 2, 1, 2,
  590.              GTK_SHRINK, GTK_SHRINK, 0, 0);
  591.    gtk_widget_show (curl_pixmap_widget);
  592.  
  593.    {
  594.      gint i;
  595.      gchar *name[] =
  596.      {
  597.        N_("Upper Left"),
  598.        N_("Upper Right"),
  599.        N_("Lower Left"),
  600.        N_("Lower Right")
  601.      };
  602.  
  603.      button = NULL;
  604.      for (i = 0; i < 4; i++)
  605.        {
  606.      button = gtk_radio_button_new_with_label
  607.        ((button == NULL) ?
  608.         NULL : gtk_radio_button_group (GTK_RADIO_BUTTON (button)),
  609.         gettext(name[i]));
  610.      gtk_toggle_button_set_active
  611.        (GTK_TOGGLE_BUTTON (button),
  612.         (i == 0 ? curl.do_upper_left : i == 1 ? curl.do_upper_right :
  613.          i == 2 ? curl.do_lower_left : curl.do_lower_right));
  614.  
  615.      gtk_signal_connect (GTK_OBJECT (button), "toggled",
  616.                  GTK_SIGNAL_FUNC (dialog_toggle_update),
  617.                  GINT_TO_POINTER (i));
  618.  
  619.      gtk_table_attach (GTK_TABLE (table), button,
  620.                (i % 2) ? 2 : 0, (i % 2) ? 3 : 1,
  621.                (i < 2) ? 0 : 2, (i < 2) ? 1 : 3,
  622.                GTK_SHRINK, GTK_SHRINK, 0, 0);
  623.  
  624.      gtk_widget_show (button);
  625.        }
  626.    }
  627.  
  628.    gtk_widget_show (table);
  629.    gtk_widget_show (frame);
  630.  
  631.    frame = gtk_frame_new (_("Curl Orientation"));
  632.    gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  633.    gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  634.  
  635.    hbox = gtk_hbox_new (FALSE, 4);
  636.    gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
  637.    gtk_container_add (GTK_CONTAINER (frame), hbox);
  638.  
  639.    {
  640.      gint i;
  641.      gchar *name[] =
  642.      {
  643.        N_("Horizontal"),
  644.        N_("Vertical")
  645.      };
  646.  
  647.      button = NULL;
  648.      for (i = 0; i < 2; i++)
  649.        {
  650.      button = gtk_radio_button_new_with_label
  651.        ((button == NULL) ?
  652.         NULL : gtk_radio_button_group (GTK_RADIO_BUTTON (button)),
  653.         gettext(name[i]));
  654.      gtk_toggle_button_set_active
  655.        (GTK_TOGGLE_BUTTON (button),
  656.         (i == 0 ? curl.do_horizontal : curl.do_vertical));
  657.  
  658.      gtk_signal_connect (GTK_OBJECT (button), "toggled",
  659.                  GTK_SIGNAL_FUNC (dialog_toggle_update),
  660.                  GINT_TO_POINTER (i + 5));
  661.  
  662.      gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
  663.      gtk_widget_show (button);
  664.        }
  665.    }
  666.  
  667.    gtk_widget_show (hbox);
  668.    gtk_widget_show (frame);
  669.  
  670.    shade_button = gtk_check_button_new_with_label (_("Shade under Curl"));
  671.    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (shade_button),
  672.                  curl.do_shade_under ? TRUE : FALSE);
  673.    gtk_signal_connect (GTK_OBJECT (shade_button), "toggled",
  674.                GTK_SIGNAL_FUNC (dialog_toggle_update),
  675.                (gpointer) 8);
  676.    gtk_box_pack_start (GTK_BOX (vbox), shade_button, FALSE, FALSE, 0);
  677.    gtk_widget_show (shade_button);
  678.  
  679.    gradient_button =
  680.      gtk_check_button_new_with_label (_("Use Current Gradient\n"
  681.                     "instead of FG/BG-Color"));
  682.    gtk_label_set_justify (GTK_LABEL (GTK_BIN (gradient_button)->child),
  683.               GTK_JUSTIFY_LEFT);
  684.    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gradient_button),
  685.                  curl.do_curl_gradient ? TRUE : FALSE);
  686.    gtk_signal_connect (GTK_OBJECT (gradient_button), "toggled",
  687.                GTK_SIGNAL_FUNC (dialog_toggle_update),
  688.                (gpointer) 9);
  689.    gtk_box_pack_start (GTK_BOX (vbox), gradient_button, FALSE, FALSE, 0);
  690.    gtk_widget_show (gradient_button);
  691.  
  692.    frame = gtk_frame_new (_("Curl Opacity"));
  693.    gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  694.    gtk_widget_show (frame);
  695.  
  696.    vbox2 = gtk_vbox_new (FALSE, 0);
  697.    gtk_container_set_border_width (GTK_CONTAINER (vbox2), 2);
  698.    gtk_container_add (GTK_CONTAINER (frame), vbox2);
  699.    gtk_widget_show (vbox2);
  700.  
  701.    adjustment = gtk_adjustment_new (curl.do_curl_opacity * 100, 0.0, 100.0,
  702.                     1.0, 1.0, 0.0);
  703.    gtk_signal_connect (adjustment, "value_changed",
  704.                GTK_SIGNAL_FUNC (dialog_scale_update),
  705.                &(curl.do_curl_opacity));
  706.  
  707.    scale = gtk_hscale_new (GTK_ADJUSTMENT (adjustment));
  708.    gtk_widget_set_usize (GTK_WIDGET (scale), 150, 30);
  709.    gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
  710.    gtk_scale_set_digits (GTK_SCALE (scale), 0);
  711.    gtk_scale_set_draw_value (GTK_SCALE (scale), TRUE);
  712.    gtk_box_pack_start (GTK_BOX (vbox2), scale, TRUE, FALSE, 0);
  713.    gtk_widget_show (scale);
  714.  
  715.    gtk_widget_show (dialog);
  716.  
  717.    gtk_main ();
  718.    gdk_flush ();
  719.  
  720.    return curl_run;
  721. }
  722.  
  723. static void
  724. init_calculation (void)
  725. {
  726.   gdouble      k;
  727.   gdouble      alpha, beta;
  728.   gdouble      angle;
  729.   GimpVector2  v1, v2;
  730.   gint32      *image_layers, nlayers;
  731.  
  732.   gimp_layer_add_alpha (drawable->id);
  733.   drawable = gimp_drawable_get (drawable->id);
  734.  
  735.   /* Image parameters */
  736.  
  737.   /* Determine Position of original Layer in the Layer stack. */
  738.  
  739.   image_layers = gimp_image_get_layers (image_id, &nlayers);
  740.   drawable_position = 0;
  741.   while (drawable_position < nlayers &&
  742.      image_layers[drawable_position] != drawable->id)
  743.     drawable_position++;
  744.   if (drawable_position >= nlayers)
  745.     {
  746.       fprintf (stderr, "Fatal error: drawable not found in layer stack.\n");
  747.       drawable_position = 0;
  748.    }
  749.   /* Get the bounds of the active selection */
  750.   gimp_drawable_mask_bounds (drawable->id, &sel_x1, &sel_y1, &sel_x2, &sel_y2);
  751.  
  752.   true_sel_width = sel_x2 - sel_x1;
  753.   true_sel_height = sel_y2 - sel_y1;
  754.   if (curl.do_vertical)
  755.     {
  756.       sel_width = true_sel_width;
  757.       sel_height = true_sel_height;
  758.     }
  759.   else
  760.     {
  761.       sel_width = true_sel_height;
  762.       sel_height = true_sel_width;
  763.     }
  764.  
  765.   /* Circle parameters */
  766.  
  767.   alpha = atan ((double) sel_height / sel_width);
  768.   beta = alpha / 2.0;
  769.   k = sel_width / ((G_PI + alpha) * sin (beta) + cos (beta));
  770.   gimp_vector2_set (¢er, k * cos (beta), k * sin (beta));
  771.   radius = center.y;
  772.  
  773.   /* left_tangent  */
  774.  
  775.   gimp_vector2_set (&left_tangent, radius * -sin (alpha), radius * cos (alpha));
  776.   gimp_vector2_add (&left_tangent, &left_tangent, ¢er);
  777.  
  778.   /* right_tangent */
  779.  
  780.   gimp_vector2_sub (&v1, &left_tangent, ¢er);
  781.   gimp_vector2_set (&v2, sel_width - center.x, sel_height - center.y);
  782.   angle = -2.0 * acos (gimp_vector2_inner_product (&v1, &v2) /
  783.                (gimp_vector2_length (&v1) * gimp_vector2_length (&v2)));
  784.   gimp_vector2_set (&right_tangent,
  785.             v1.x * cos (angle) + v1.y * -sin (angle),
  786.             v1.x * sin (angle) + v1.y * cos (angle));
  787.   gimp_vector2_add (&right_tangent, &right_tangent, ¢er);
  788.  
  789.   /* Slopes */
  790.  
  791.   diagl_slope = (double) sel_width / sel_height;
  792.   diagr_slope = (sel_width - right_tangent.x) / (sel_height - right_tangent.y);
  793.   diagb_slope = (right_tangent.y - left_tangent.y) / (right_tangent.x - left_tangent.x);
  794.   diagm_slope = (sel_width - center.x) / sel_height;
  795.  
  796.   /* Colors */
  797.  
  798.   gimp_palette_get_foreground (&fore_color[0], &fore_color[1], &fore_color[2]);
  799.   gimp_palette_get_background (&back_color[0], &back_color[1], &back_color[2]);
  800. }
  801.  
  802. static void
  803. do_curl_effect (void)
  804. {
  805.   gint         x, y, color_image;
  806.   gint         x1, y1, k;
  807.   guint        alpha_pos, progress, max_progress;
  808.   gdouble      intensity, alpha, beta;
  809.   GimpVector2  v, dl, dr;
  810.   gdouble      dl_mag, dr_mag, angle, factor;
  811.   guchar      *pp, *dest, fore_grayval, back_grayval;
  812.   guchar      *gradsamp;
  813.   GimpPixelRgn    dest_rgn;
  814.   gpointer     pr;
  815.   guchar      *grad_samples = NULL;
  816.  
  817.   color_image = gimp_drawable_is_rgb (drawable->id);
  818.   curl_layer =
  819.     gimp_drawable_get (gimp_layer_new (image_id,
  820.                        _("Curl Layer"),
  821.                        true_sel_width,
  822.                        true_sel_height,
  823.                        color_image ? GIMP_RGBA_IMAGE : GIMP_GRAYA_IMAGE,
  824.                        100, GIMP_NORMAL_MODE));
  825.   gimp_image_add_layer (image_id, curl_layer->id, drawable_position);
  826.   curl_layer_ID = curl_layer->id;
  827.  
  828.   gimp_drawable_offsets (drawable->id, &x1, &y1);
  829.   gimp_layer_set_offsets (curl_layer->id, sel_x1 + x1, sel_y1 + y1);
  830.   gimp_tile_cache_ntiles (2 * (curl_layer->width / gimp_tile_width () + 1));
  831.  
  832.   /* Clear the newly created layer */
  833.   gimp_pixel_rgn_init (&dest_rgn, curl_layer,
  834.                0, 0, true_sel_width, true_sel_height, TRUE, FALSE);
  835.   for (pr = gimp_pixel_rgns_register (1, &dest_rgn);
  836.        pr != NULL;
  837.        pr = gimp_pixel_rgns_process (pr))
  838.     {
  839.       dest = dest_rgn.data;
  840.       for (y1 = dest_rgn.y; y1 < dest_rgn.y + dest_rgn.h; y1++)
  841.     {
  842.       pp = dest;
  843.       for (x1 = dest_rgn.x; x1 < dest_rgn.x + dest_rgn.w; x1++)
  844.         {
  845.           for (k = 0; k < dest_rgn.bpp; k++)
  846.         pp[k] = 0;
  847.           pp += dest_rgn.bpp;
  848.         }
  849.       dest += dest_rgn.rowstride;
  850.     }
  851.     }
  852.  
  853.   gimp_drawable_flush (curl_layer);
  854.   gimp_drawable_update (curl_layer->id,
  855.             0, 0, curl_layer->width, curl_layer->height);
  856.  
  857.   gimp_pixel_rgn_init (&dest_rgn, curl_layer,
  858.                0, 0, true_sel_width, true_sel_height, TRUE, TRUE);
  859.  
  860.   /* Init shade_under */
  861.   gimp_vector2_set (&dl, -sel_width, -sel_height);
  862.   dl_mag = gimp_vector2_length (&dl);
  863.   gimp_vector2_set (&dr,
  864.             -(sel_width - right_tangent.x),
  865.             -(sel_height - right_tangent.y));
  866.   dr_mag = gimp_vector2_length (&dr);
  867.   alpha = acos (gimp_vector2_inner_product (&dl, &dr) / (dl_mag * dr_mag));
  868.   beta=alpha / 2;
  869.  
  870.   /* Init shade_curl */
  871.  
  872.   fore_grayval = INTENSITY (fore_color[0], fore_color[1], fore_color[2]);
  873.   back_grayval = INTENSITY (back_color[0], back_color[1], back_color[2]);
  874.  
  875.   /* Gradient Samples */
  876.   if (curl.do_curl_gradient)
  877.     grad_samples = get_samples (curl_layer);
  878.  
  879.   max_progress = 2 * sel_width * sel_height;
  880.   progress = 0;
  881.  
  882.   alpha_pos = dest_rgn.bpp - 1;
  883.  
  884.   /* Main loop */
  885.   for (pr = gimp_pixel_rgns_register (1, &dest_rgn);
  886.        pr != NULL;
  887.        pr = gimp_pixel_rgns_process (pr))
  888.     {
  889.       dest = dest_rgn.data;
  890.  
  891.       for (y1 = dest_rgn.y; y1 < dest_rgn.y + dest_rgn.h; y1++)
  892.     {
  893.       pp = dest;
  894.       for (x1 = dest_rgn.x; x1 < dest_rgn.x + dest_rgn.w; x1++)
  895.         {
  896.           /* Map coordinates to get the curl correct... */
  897.           if (curl.do_horizontal)
  898.         {
  899.           x = (curl.do_lower_right || curl.do_lower_left) ? y1 : sel_width - 1 - y1;
  900.           y = (curl.do_upper_left || curl.do_lower_left) ? x1 : sel_height - 1 - x1;
  901.         }
  902.           else
  903.         {
  904.           x = (curl.do_upper_right || curl.do_lower_right) ? x1 : sel_width - 1 - x1;
  905.           y = (curl.do_upper_left || curl.do_upper_right) ? y1 : sel_height - 1 - y1;
  906.         }
  907.           if (left_of_diagl (x, y))
  908.         { /* uncurled region */
  909.           for (k = 0; k <= alpha_pos; k++)
  910.             pp[k] = 0;
  911.         }
  912.           else if (right_of_diagr (x, y) ||
  913.                (right_of_diagm (x, y) &&
  914.             below_diagb (x, y) &&
  915.             !inside_circle (x, y)))
  916.         {
  917.           /* curled region */
  918.           for (k = 0; k <= alpha_pos; k++)
  919.             pp[k] = 0;
  920.         }
  921.           else
  922.         {
  923.           v.x = -(sel_width - x);
  924.           v.y = -(sel_height - y);
  925.           angle = acos (gimp_vector2_inner_product (&v, &dl) /
  926.                 (gimp_vector2_length (&v) * dl_mag));
  927.  
  928.           if (inside_circle (x, y) || below_diagb (x, y))
  929.             {
  930.               /* Below the curl. */
  931.               factor = angle / alpha;
  932.               for (k = 0; k < alpha_pos; k++)
  933.             pp[k] = 0;
  934.               pp[alpha_pos] = curl.do_shade_under ? (guchar) ((float) 255 * (float) factor) : 0;
  935.             }
  936.           else
  937.             {
  938.               /* On the curl */
  939.               if (curl.do_curl_gradient)
  940.             {
  941.               /* Calculate position in Gradient (0 <= intensity <= 1) */
  942.               intensity = (angle/alpha) + sin(G_PI*2 * angle/alpha)*0.075;
  943.               /* Check boundaries */
  944.               intensity = (intensity < 0 ? 0 : (intensity > 1 ? 1 : intensity ));
  945.               gradsamp = &grad_samples[((guint) (intensity * NGRADSAMPLES)) * dest_rgn.bpp];
  946.               if (color_image)
  947.                 {
  948.                   pp[0] = gradsamp[0];
  949.                   pp[1] = gradsamp[1];
  950.                   pp[2] = gradsamp[2];
  951.                 }
  952.               else 
  953.                 pp[0] = gradsamp[0];
  954.               pp[alpha_pos] = (guchar) ((double) gradsamp[alpha_pos] * (1 - intensity*(1-curl.do_curl_opacity)));
  955.             }
  956.               else
  957.             {
  958.               intensity = pow (sin (G_PI * angle / alpha), 1.5);
  959.               if (color_image)
  960.                 {
  961.                   pp[0] = (intensity * back_color[0] + (1 - intensity) * fore_color[0]);
  962.                   pp[1] = (intensity * back_color[1] + (1 - intensity) * fore_color[1]);
  963.                   pp[2] = (intensity * back_color[2] + (1 - intensity) * fore_color[2]);
  964.                 }
  965.               else
  966.                 pp[0] = (intensity * back_grayval + (1 - intensity) * fore_grayval);
  967.               pp[alpha_pos] = (guchar) ((double) 255 * (1 - intensity*(1-curl.do_curl_opacity)));
  968.             }
  969.             }
  970.         }
  971.           pp += dest_rgn.bpp;
  972.         }
  973.       dest += dest_rgn.rowstride;
  974.     }
  975.       progress += dest_rgn.w * dest_rgn.h;
  976.       gimp_progress_update ((double) progress / (double) max_progress);
  977.     }
  978.  
  979.   gimp_drawable_flush (curl_layer);
  980.   gimp_drawable_merge_shadow (curl_layer->id, FALSE);
  981.   gimp_drawable_update (curl_layer->id,
  982.             0, 0, curl_layer->width, curl_layer->height);
  983.   gimp_drawable_detach (curl_layer);
  984.  
  985.   if (grad_samples != NULL)
  986.     g_free (grad_samples);
  987. }
  988.  
  989. /************************************************/
  990.  
  991. static void
  992. clear_curled_region (void)
  993. {
  994.   GimpPixelRgn src_rgn, dest_rgn;
  995.   gpointer pr;
  996.   gint x, y;
  997.   guint x1, y1, i;
  998.   guchar *dest, *src, *pp, *sp;
  999.   guint alpha_pos, progress, max_progress;
  1000.  
  1001.   max_progress = 2 * sel_width * sel_height;
  1002.   progress = max_progress / 2;
  1003.  
  1004.   gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1));
  1005.   gimp_pixel_rgn_init (&src_rgn, drawable,
  1006.                sel_x1, sel_y1, true_sel_width, true_sel_height,
  1007.                FALSE, FALSE);
  1008.   gimp_pixel_rgn_init (&dest_rgn, drawable,
  1009.                sel_x1, sel_y1, true_sel_width, true_sel_height,
  1010.                TRUE, TRUE);
  1011.   alpha_pos = dest_rgn.bpp - 1;
  1012.   if (dest_rgn.bpp != src_rgn.bpp)
  1013.     fprintf (stderr, "WARNING: src_rgn.bpp != dest_rgn.bpp!\n");
  1014.   for (pr = gimp_pixel_rgns_register (2, &dest_rgn, &src_rgn);
  1015.        pr != NULL;
  1016.        pr = gimp_pixel_rgns_process (pr))
  1017.     {
  1018.       dest = dest_rgn.data;
  1019.       src = src_rgn.data;
  1020.       for (y1 = dest_rgn.y; y1 < dest_rgn.y + dest_rgn.h; y1++)
  1021.     {
  1022.       sp = src;
  1023.       pp = dest;
  1024.       for (x1 = dest_rgn.x; x1 < dest_rgn.x + dest_rgn.w; x1++)
  1025.         {
  1026.           /* Map coordinates to get the curl correct... */
  1027.           if (curl.do_horizontal)
  1028.         {
  1029.           x = (curl.do_lower_right || curl.do_lower_left) ? y1 - sel_y1 : sel_width - 1 - (y1 - sel_y1);
  1030.           y = (curl.do_upper_left || curl.do_lower_left) ? x1 - sel_x1 : sel_height - 1 - (x1 - sel_x1);
  1031.         }
  1032.           else
  1033.         {
  1034.           x = (curl.do_upper_right || curl.do_lower_right) ? x1 - sel_x1 : sel_width - 1 - (x1 - sel_x1);
  1035.           y = (curl.do_upper_left || curl.do_upper_right) ? y1 - sel_y1 : sel_height - 1 - (y1 - sel_y1);
  1036.         }
  1037.           for (i = 0; i < alpha_pos; i++)
  1038.         pp[i] = sp[i];
  1039.           if (right_of_diagr (x, y) ||
  1040.           (right_of_diagm (x, y) &&
  1041.            below_diagb (x, y) &&
  1042.            !inside_circle (x, y)))
  1043.         {
  1044.           /* Right of the curl */
  1045.           pp[alpha_pos] = 0;
  1046.         }
  1047.           else
  1048.         {
  1049.           pp[alpha_pos] = sp[alpha_pos];
  1050.         }
  1051.           pp += dest_rgn.bpp;
  1052.           sp += src_rgn.bpp;
  1053.         }
  1054.       src += src_rgn.rowstride;
  1055.       dest += dest_rgn.rowstride;
  1056.     }
  1057.       progress += dest_rgn.w * dest_rgn.h;
  1058.       gimp_progress_update ((double) progress / (double) max_progress);
  1059.     }
  1060.   gimp_drawable_flush (drawable);
  1061.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  1062.   gimp_drawable_update (drawable->id, sel_x1, sel_y1, true_sel_width, true_sel_height);
  1063.   gimp_drawable_detach (drawable);
  1064. }
  1065.  
  1066. static void
  1067. page_curl (void)
  1068. {
  1069.   gimp_undo_push_group_start (image_id);
  1070.   gimp_progress_init ( _("Page Curl..."));
  1071.   init_calculation ();
  1072.   do_curl_effect ();
  1073.   clear_curled_region ();
  1074.   gimp_undo_push_group_end (image_id);
  1075. }
  1076.  
  1077. /*
  1078.   Returns NGRADSAMPLES samples of active gradient.
  1079.   Each sample has (gimp_drawable_bpp (drawable->id)) bytes.
  1080.   "ripped" from gradmap.c.
  1081.  */
  1082. static guchar *
  1083. get_samples (GimpDrawable *drawable)
  1084.  {
  1085.   gdouble       *f_samples, *f_samp;    /* float samples */
  1086.   guchar        *b_samples, *b_samp;    /* byte samples */
  1087.   gint          bpp, color, has_alpha, alpha;
  1088.   gint          i, j;
  1089.  
  1090.   f_samples = gimp_gradients_sample_uniform (NGRADSAMPLES);
  1091.  
  1092.   bpp       = gimp_drawable_bpp (drawable->id);
  1093.   color     = gimp_drawable_is_rgb (drawable->id);
  1094.   has_alpha = gimp_drawable_has_alpha (drawable->id);
  1095.   alpha     = (has_alpha ? bpp - 1 : bpp);
  1096.  
  1097.   b_samples = g_new (guchar, NGRADSAMPLES * bpp);
  1098.  
  1099.   for (i = 0; i < NGRADSAMPLES; i++)
  1100.     {
  1101.       b_samp = &b_samples[i * bpp];
  1102.       f_samp = &f_samples[i * 4];
  1103.       if (color)
  1104.         for (j = 0; j < 3; j++)
  1105.           b_samp[j] = f_samp[j] * 255;
  1106.       else
  1107.         b_samp[0] = INTENSITY (f_samp[0], f_samp[1], f_samp[2]) * 255;
  1108.  
  1109.       if (has_alpha)
  1110.         b_samp[alpha] = f_samp[3] * 255;
  1111.     }
  1112.  
  1113.   g_free (f_samples);
  1114.  
  1115.   return b_samples;
  1116. }
  1117.